package cn.ddiancan.auth.service.userdetails;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import cn.ddiancan.auth.constant.RequestSourceEnum;
import cn.ddiancan.auth.constant.RequestTypeEnum;
import cn.ddiancan.auth.context.AuthContext;
import cn.ddiancan.xddcloud.common.constants.XddHeaderConstant;
import cn.ddiancan.xddcloud.common.entity.CommonResult;
import cn.ddiancan.xddcloud.common.entity.UserVO;
import cn.ddiancan.xddcloud.ddccore.cache.XddCloudCacheClient;
import cn.ddiancan.xddcloud.psc.base.domain.CkMerchantEntity;
import cn.ddiancan.xddcloud.psc.base.service.UserClientService;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;


@Slf4j
@RequiredArgsConstructor
public class XddcloudUserDetailsImpl implements UserDetailsService {

    @Setter
    private RequestSourceEnum requestSource = RequestSourceEnum.PASSWORD;

    @Setter
    private RequestTypeEnum loginType = RequestTypeEnum.user;

    private final static String USER_KEY = XddHeaderConstant.PERMISSION_CACHE_PREFIX;

    private final static String SPER = "_";

    private final UserClientService userClientService;

    private final XddCloudCacheClient xddCloudCacheClient;

    /**
     * 加载用户
     *
     * @param userAccount the username identifying the user whose data is required.
     * @return UserDetails 用户
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String userAccount) throws UsernameNotFoundException {
        return switch (requestSource) {
            case USER_CODE -> loadUserByTempAuthCode(userAccount);
            case VERIFY_CODE -> loadUserByPhoneOrAccount(userAccount);
            default -> loadUserByUserAccount(userAccount);
        };

    }

    private UserDetails loadUserByPhoneOrAccount(String phoneOrAccount) {
        try {
            UserVO userByMobile = switch (loginType) {
                case merchant -> getMerchantInfoByPhoneOrAccount(phoneOrAccount);
                default -> getPscUserByPhoneOrAccount(phoneOrAccount);
            };
            return new User(userByMobile.getUserAccount(), userByMobile.getPassword(), AuthorityUtils.NO_AUTHORITIES);
        } catch (Exception e) {
            log.error("远程查询用户信息出错:{}", e.getMessage(), e);
            throw new UsernameNotFoundException("用户不存在或系统出错，请联系管理员");
        }
    }

    private UserVO getMerchantInfoByPhoneOrAccount(String phoneOrAccount) {
        String cacheKey = USER_KEY.concat(loginType.name()).concat(SPER).concat(phoneOrAccount);
        UserVO userVO = xddCloudCacheClient.get(cacheKey);
        userVO = Optional.ofNullable(userVO).orElseGet(() -> {
            CommonResult<CkMerchantEntity> ckMerchantEntityCommonResult =
                userClientService.queryByMobile(phoneOrAccount);
            if (Objects.isNull(ckMerchantEntityCommonResult) ||
                Objects.isNull(ckMerchantEntityCommonResult.getData())) {
                throw new RuntimeException("帐号:" + phoneOrAccount + "未注册");
            }
            CkMerchantEntity data = ckMerchantEntityCommonResult.getData();
            return Optional.ofNullable(data).map(ckMerchantEntity -> {
                UserVO user = new UserVO();
                Map<String,Object> properties = new HashMap<>();
                properties.put("isPreOrder", Objects.nonNull(ckMerchantEntity.getIsPreOrder()) ? ckMerchantEntity.getIsPreOrder() : 1);
                properties.put("isDel", ckMerchantEntity.getIsDel());
                properties.put("isSelf", ckMerchantEntity.getIsSelf());
                properties.put("isSelfDelivery", ckMerchantEntity.getIsSelfDelivery());
                user.setProperties(properties);
                user.setUserAccount(ckMerchantEntity.getUsername());
                user.setPassword(ckMerchantEntity.getPassword());
                user.setUserName(ckMerchantEntity.getRealname());
                user.setPhone(ckMerchantEntity.getMobile());
                user.setUserId(ckMerchantEntity.getId());
                user.setStoreAddress(ckMerchantEntity.getAddressName());
                user.setCheckInTime(ckMerchantEntity.getCheckInTime());
                user.setCheckInDay(ckMerchantEntity.getCheckInDay());
                user.setCustomerServiceTelephone(ckMerchantEntity.getCustomerServiceTelephone());
                return user;
            }).orElse(new UserVO());
        });
        xddCloudCacheClient.put(cacheKey, userVO);
        AuthContext.setAuthUser(userVO);
        return userVO;
    }

    /**
     * 加载用户
     *
     * @param userAccount the userAccount identifying the user whose data is required.
     * @return UserDetails 用户
     * @throws UsernameNotFoundException
     */
    private UserDetails loadUserByUserAccount(String userAccount) throws UsernameNotFoundException {
        try {
            UserVO userByUserAccount = switch (loginType) {
                case merchant -> getMerchantInfoByPhoneOrAccount(userAccount);
                default -> getPscUserByPhoneOrAccount(userAccount);
            };
            return new User(userByUserAccount.getUserAccount(), userByUserAccount.getPassword(),
                AuthorityUtils.NO_AUTHORITIES);
        } catch (Exception e) {
            log.error("远程查询用户信息出错:{}", e.getMessage(), e);
            throw new UsernameNotFoundException("用户不存在或系统出错，请联系管理员");
        }
    }

    /**
     * 加载用户
     *
     * @param tempAuthCode the tempAuthCode identifying the user whose data is required.
     * @return UserDetails 用户
     * @throws UsernameNotFoundException
     */
    private UserDetails loadUserByTempAuthCode(String tempAuthCode) throws UsernameNotFoundException {
        try {
            UserVO userByTempAuthCode = getPscUserByUserUserCode(tempAuthCode);
            return new User(userByTempAuthCode.getUserAccount(), userByTempAuthCode.getPassword(),
                AuthorityUtils.NO_AUTHORITIES);
        } catch (Exception e) {
            log.error("远程查询用户信息出错:{}", e.getMessage(), e);
            throw new UsernameNotFoundException("远程用户不存在或系统出错，请联系管理员");
        }
    }

    /**
     * 获取用户信息
     *
     * @param phoneOrAccount 账号或手机号
     * @return UserVO
     */
    public UserVO getPscUserByPhoneOrAccount(String phoneOrAccount) {
        String key = USER_KEY.concat(loginType.name()).concat(SPER).concat(phoneOrAccount);
        UserVO remoteUser = xddCloudCacheClient.get(key);
        remoteUser = Optional.ofNullable(remoteUser).orElseGet(() -> {
            CommonResult<UserVO> userPermissionByAccount = userClientService.getUserPermissionByAccount(phoneOrAccount);
            UserVO pscUserByAccount = Optional.ofNullable(userPermissionByAccount).orElse(CommonResult.success()).getData();
            if (Objects.nonNull(pscUserByAccount)) {
                xddCloudCacheClient.put(key, pscUserByAccount);
            }
            return pscUserByAccount;
        });
        AuthContext.setAuthUser(remoteUser);
        return remoteUser;
    }

    /**
     * 获取用户信息
     *
     * @param userCode 钉钉临时授权码
     * @return UserVO
     */
    public UserVO getPscUserByUserUserCode(String userCode) {
        String key = USER_KEY + userCode;
        UserVO remoteUser = xddCloudCacheClient.get(key);
        remoteUser = Optional.ofNullable(remoteUser).orElseGet(() -> {
            UserVO userByTempAuthCode = getPscUserByTempAuthCode(userCode);
            if (Objects.nonNull(userByTempAuthCode)) {
                xddCloudCacheClient.put(key, userByTempAuthCode);
            }
            return userByTempAuthCode;
        });
        AuthContext.setAuthUser(remoteUser);
        return remoteUser;
    }

    /**
     * 获取用户信息
     *
     * @param tempAuthCode tempAuthCode
     * @return UserVO
     */
    public UserVO getPscUserByTempAuthCode(String tempAuthCode) {
        return Optional.ofNullable(userClientService.getUserPermissionByTempAuthCode(tempAuthCode).getData())
            .orElse(new UserVO());
    }
}
